package com.fable.sp.core.domain;

import com.fable.sp.core.util.JSONType;
import com.fable.sp.core.util.SnowflakeGenerator;
import com.google.common.collect.Maps;
import com.google.common.hash.Funnel;
import com.google.common.hash.Hashing;
import com.google.common.hash.PrimitiveSink;
import com.slyak.spring.jpa.Status;
import org.hibernate.annotations.GenericGenerator;
import org.hibernate.annotations.Type;
import org.joda.time.DateTime;
import org.springframework.data.domain.Auditable;
import org.springframework.data.domain.Persistable;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
import org.springframework.util.CollectionUtils;

import javax.persistence.*;
import java.util.Map;

@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
public class BaseEntity implements Persistable<Long>, Auditable<String, Long> {

    private static final long serialVersionStringID = 141481953116476081L;

    @Column(name = "created_by")
    private String createdBy;

    @Column(name = "last_modified_by")
    private String lastModifiedBy;

    @Column(name = "created_date")
    private long createdDate = 0;

    @Column(name = "last_modified_date")
    private long lastModifiedDate = 0;

    @Version
    @Column(name = "version0")
    private long version;

    @Column(name = "status0")
    private Status status = Status.ENABLED;

    @Id
    @GenericGenerator(name = "snowflake", strategy = SnowflakeGenerator.TYPE)
    @GeneratedValue(generator = "snowflake")
    private Long id;

    @Type(type = JSONType.TYPE)
    @Column(length = 2000)
    private Map<String, String> attributes = Maps.newHashMap();

    @Transient
    private Object creator;

    public Map<String, String> getAttributes() {
        return attributes;
    }

    public void setAttributes(Map<String, String> attributes) {
        this.attributes = attributes;
    }

    public boolean containsAttribute(String name) {
        if (CollectionUtils.isEmpty(attributes)) {
            return false;
        }
        return attributes.containsKey(name);
    }

    public String getAttribute(String name) {
        if (CollectionUtils.isEmpty(attributes)) {
            return null;
        }
        return attributes.get(name);
    }

    public void setAttribute(String name, String value) {
        if (name == null) {
            return;
        }
        Map<String, String> attributes = getAttributes();
        attributes = attributes == null ? Maps.<String, String>newHashMap() : attributes;
        attributes.put(name, value);
    }

    @Override
    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    @Override
    public boolean isNew() {
        return null == getId();
    }

    public String getCreatedBy() {
        return createdBy;
    }

    public void setCreatedBy(final String createdBy) {
        this.createdBy = createdBy;
    }

    public String getLastModifiedBy() {
        return lastModifiedBy;
    }

    public void setLastModifiedBy(final String lastModifiedBy) {
        this.lastModifiedBy = lastModifiedBy;
    }

    public long getVersion() {
        return version;
    }

    public void setVersion(long version) {
        this.version = version;
    }

    public Status getStatus() {
        return status;
    }

    public void setStatus(Status status) {
        this.status = status;
    }

    public DateTime getLastModifiedDate() {

        return 0 == lastModifiedDate ? null : new DateTime(lastModifiedDate);
    }

    public void setLastModifiedDate(final DateTime lastModifiedDate) {
        this.lastModifiedDate = null == lastModifiedDate ? 0 : lastModifiedDate.toDate().getTime();
    }

    public DateTime getCreatedDate() {
        return 0 == createdDate ? null : new DateTime(createdDate);
    }

    public void setCreatedDate(final DateTime createdDate) {
        this.createdDate = null == createdDate ? 0 : createdDate.toDate().getTime();
    }

    public Object getCreator() {
        return creator;
    }

    public void setCreator(Object creator) {
        this.creator = creator;
    }

    @Override
    public int hashCode() {
        if (id == null) {
            return super.hashCode();
        }
        return Hashing.sipHash24().hashObject(id, new Funnel<Long>() {
            @Override
            public void funnel(Long from, PrimitiveSink into) {
                into.putLong(from);
            }
        }).asInt();
    }
}
